package org.geoserver.wms.map;

import com.southzn.smartgis.wraper.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.IOException;
import java.util.*;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import org.geoserver.wms.WMSMapContent;
import org.geotools.data.FeatureSource;
import org.geotools.data.Query;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.filter.IllegalFilterException;
import org.geotools.geometry.jts.Decimator;
import org.geotools.geometry.jts.LiteShape2;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.map.Layer;
import org.geotools.referencing.CRS;
import org.geotools.referencing.operation.transform.ProjectiveTransform;
import org.geotools.renderer.lite.StreamingRenderer;
import org.geotools.util.factory.Hints;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.geom.Polygon;
import org.opengis.feature.Feature;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.FeatureType;
import org.opengis.feature.type.GeometryDescriptor;
import org.opengis.filter.Filter;
import org.opengis.filter.expression.PropertyName;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.NoninvertibleTransformException;
import org.opengis.referencing.operation.TransformException;

public class CustomRenderer extends StreamingRenderer {
    // 自定义样式参数
    private static final Color HIGHLIGHT_COLOR = new Color(255, 0, 0, 150); // 半透明红色
    private static final Color DEFAULT_FILL = new Color(50, 150, 255, 100);
    private ExecutorService threadPool; // 线程池
    private String workspace = "";
    private String oldworkspace = "";

    private double currentSimplificationTolerance = 0.025;

    @Override
    public void setThreadPool(ExecutorService threadPool) {
        this.threadPool = threadPool;
    }

    private ExecutorService executor =
            Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    private ExecutorService executorService =
            Executors.newFixedThreadPool(
                    Runtime.getRuntime().availableProcessors(), new OrderedThreadFactory());

    static {
        System.loadLibrary("sv_render_symbol_wrapper_java");
        System.out.println("ce3333");
    }

    private MathTransform finalAtTransform;

    private CoordinateReferenceSystem coodinate_system;
    private WMSMapContent wmsMapContent;

    private SGRenderSymbol list_render = null;

    public void paint(
            Graphics2D graphics,
            Rectangle paintArea,
            ReferencedEnvelope mapBounds,
            WMSMapContent mapContent) {

        wmsMapContent = mapContent;
        // 1. 保存原始绘图配置
        Color originalColor = graphics.getColor();
        Stroke originalStroke = graphics.getStroke();
        if (list_render == null) {
            list_render = new SGRenderSymbol();
            boolean is_ok = list_render.readSymbolContext(this.workspace);
            if (is_ok == false) {
                System.out.println("read symbol failed");
            }
            this.oldworkspace = this.workspace;
        }

        if (!this.oldworkspace.isEmpty() && this.workspace != this.oldworkspace) {
            list_render.readSymbolContext(this.workspace);
            this.oldworkspace = this.workspace;
        }

        double mapScale = mapContent.getScaleDenominator();
        list_render.renderStart(
                "",
                mapScale,
                paintArea.height,
                paintArea.width,
                mapBounds.getMinX(),
                mapBounds.getMinY(),
                mapBounds.getMaxX(),
                mapBounds.getMaxY());

        try {

            // 4. 二次渲染：添加自定义效果
            renderCustom(graphics, mapContent, paintArea, mapBounds);

        } finally {
            // 恢复原始配置
            //   graphics.setColor(originalColor);
            //  graphics.setStroke(originalStroke);
        }
    }

    private void renderCustom(
            Graphics2D graphics,
            WMSMapContent mapContent,
            Rectangle paintArea,
            ReferencedEnvelope bounds) {
        // 创建图层渲染队列

        List<Callable<Void>> tasks = new ArrayList<>();
        AffineTransform worldToScreen = mapContent.getRenderingTransform();

        MathTransform fullTransform = null;
        MathTransform atTransform = ProjectiveTransform.create(worldToScreen);

        coodinate_system = mapContent.getCoordinateReferenceSystem();
        finalAtTransform = atTransform;

        List<Future<?>> futures = new ArrayList<>(mapContent.layers().size());

        // 遍历所有图层
        for (Layer layer : mapContent.layers()) {

            futures.add(
                    executorService.submit(
                            () -> {
                                Graphics2D threadGraphics = (Graphics2D) graphics.create();
                                try {
                                    renderLayer(threadGraphics, layer, list_render, bounds);
                                } finally {
                                    threadGraphics.dispose();
                                }
                                return null;
                            }));
        }

        // 按提交顺序等待完成（关键！）
        for (Future<?> future : futures) {
            try {
                future.get(); // 按提交顺序阻塞等待
            } catch (InterruptedException | ExecutionException e) {
                Thread.currentThread().interrupt();
                throw new RuntimeException("Rendering interrupted", e);
            }
        }
    }

    public PropertyName getGeometryPropertyName(FeatureSource<?, ?> featureSource) {
        // 获取要素类型
        FeatureType featureType = featureSource.getSchema();

        // 获取默认几何属性描述符
        GeometryDescriptor geomDescriptor = featureType.getGeometryDescriptor();
        if (geomDescriptor == null) {
            return null; // 无几何属性
        }

        // 创建PropertyName对象（几何属性名称标识）
        return filterFactory.property(geomDescriptor.getLocalName());
    }

    private Filter createBBoxFilters2(FeatureSource<?, ?> featureSource, ReferencedEnvelope bbox)
            throws IllegalFilterException, FactoryException, TransformException {
        // if there are no bboxes to render then use Filter.EXCLUDE as there is no clear way
        // to return
        if (bbox.isEmpty()) {
            return Filter.EXCLUDE;
        }
        // 需要转成目标坐标系;
        FeatureType featureType = featureSource.getSchema();
        GeometryDescriptor geomDescriptor = featureType.getGeometryDescriptor();
        if (geomDescriptor == null) {
            return Filter.EXCLUDE; // 无几何属性
        }
        CoordinateReferenceSystem featureCrs =
                featureSource.getSchema().getCoordinateReferenceSystem();

        bbox = bbox.transform(featureCrs, true);
        // 创建PropertyName对象（几何属性名称标识）
        PropertyName attribute_name = filterFactory.property(geomDescriptor.getLocalName());

        Filter gfilter = filterFactory.bbox(attribute_name, bbox);
        return gfilter;
    }

    private void renderLayer(
            Graphics2D g, Layer layer, SGRenderSymbol renderSymbol, ReferencedEnvelope map_bounds) {
        try {
            // 获取当前比例尺
            double currentScale = wmsMapContent.getScaleDenominator();

            // 获取图层要素源并判断几何类型
            FeatureSource<?, ?> source = layer.getFeatureSource();
            FeatureType featureType = source.getSchema();
            GeometryDescriptor geomDescriptor = featureType.getGeometryDescriptor();

            // 如果当前比例尺大于3000并且图层类型为点，则直接退出
            double show_max_point_scale = list_render.showMaxPointScale();
            if (currentScale > show_max_point_scale && geomDescriptor != null) {
                Class<?> geometryType = geomDescriptor.getType().getBinding();
                if (Point.class.isAssignableFrom(geometryType)
                        || MultiPoint.class.isAssignableFrom(geometryType)) {
                    System.out.println("跳过点图层渲染: " + layer.getTitle() + ", 比例尺: " + currentScale);
                    return;
                }
            }

            SVRenderLayer render_layer = list_render.renderLayer(layer.getTitle());

            render_layer.renderStartLayer();
            long start_time = System.currentTimeMillis();

            Decimator d = new Decimator(-1, -1);

            List<String> attriutes = new ArrayList<>();

            String strAttr = render_layer.attributes();

            if (!strAttr.isEmpty()) {
                List<String> listAttr = Arrays.asList(strAttr.split(","));
                if (featureType instanceof SimpleFeatureType) {
                    SimpleFeatureType sft = (SimpleFeatureType) featureType;
                    List<AttributeDescriptor> attributes = sft.getAttributeDescriptors();

                    // 遍历字段属性
                    for (AttributeDescriptor ad : attributes) {
                        String attributeName = ad.getLocalName(); // 字段名
                        boolean res = listAttr.contains(attributeName);
                        if (res) {
                            attriutes.add(attributeName);
                        }
                    }
                }
            }
            if (attriutes.isEmpty()) {
                System.out.println("No attributes found");
            }

            Filter filter = createBBoxFilters2(source, map_bounds);
            Query query = new Query(source.getSchema().getName().getLocalPart());

            CoordinateReferenceSystem crs = geomDescriptor.getType().getCoordinateReferenceSystem();
            String geomFieldName = geomDescriptor.getLocalName();
            PropertyName attribute_name = filterFactory.property(geomFieldName);
            attriutes.add(attribute_name.getPropertyName());
            // 只查询必要的属性;
            query.setPropertyNames(attriutes);
            query.setFilter(filter);

            Hints hints =
                    new org.geotools.util.factory.Hints(
                            org.geotools.data.Query.INCLUDE_MANDATORY_PROPS,
                            Boolean.FALSE // 只查询必需属性
                            );
            // 启用几何简化;
            // hints.put(Hints.GEOMETRY_SIMPLIFICATION,1.0);
            double pixelSize = currentScale / (96 * 39.37); // 96 DPI，39.37 英寸/米
            double tolerance = pixelSize * 0.5; // 0.5 像素的地理距离

            //  hints.put(Hints.GEOMETRY_DISTANCE, tolerance); // 简化容差
            hints.put(Hints.FEATURE_2D, Boolean.TRUE);

            query.setHints(hints);
            //  hints.put(Hints.SCREENMAP, new ScreenMap(0, 0, width, height));
            query.setHints(hints);
            // 设置查询的最大要素个数;
            query.setMaxFeatures(30000);

            FeatureCollection features = source.getFeatures(query);

            // 性能优化：预先计算坐标转换，避免在循环中重复判断
            MathTransform transform = CRS.findMathTransform(crs, coodinate_system);
            boolean needsTransform = transform != null && !transform.isIdentity();

            // 性能优化：使用 try-with-resources 确保迭代器正确关闭，避免资源泄漏
            FeatureIterator<?> featureIterator = features.features();
            try {
                while (featureIterator.hasNext()) {
                    Feature feature = featureIterator.next();
                    FeatureType schema = feature.getType();

                    StringMap stringMap = new StringMap();
                    SimpleFeature featureTemp = (SimpleFeature) feature;
                    List<AttributeDescriptor> attributeDescriptors =
                            featureTemp.getType().getAttributeDescriptors();
                    for (AttributeDescriptor attr : attributeDescriptors) {
                        String fieldName = attr.getLocalName();
                        if (!fieldName.equals(geomFieldName)) {
                            Object obj = featureTemp.getAttribute(fieldName);
                            if (obj != null) {
                                String value = obj.toString();
                                stringMap.put(fieldName, value);
                            }
                        }
                    }

                    Geometry geom = (Geometry) featureTemp.getDefaultGeometry();

                    LiteShape2 liteShape = new LiteShape2(geom, transform, d, false, false);

                    // 修复2：使用instanceof替代字符串比较
                    if (geom instanceof LineString || geom instanceof MultiLineString) {
                        renderDefaultLine(g, render_layer, liteShape, stringMap);
                    } else if (geom instanceof Polygon || geom instanceof MultiPolygon) {
                        renderDefaultPolygon(g, render_layer, liteShape, stringMap);
                    } else {
                        renderDefaultPoint(g, render_layer, liteShape, stringMap);
                    }
                }
            } finally {
                // 性能优化：确保迭代器在任何情况下都会被关闭
                featureIterator.close();
            }

            System.out.println("render time" + (System.currentTimeMillis() - start_time));
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (FactoryException e) {
            throw new RuntimeException(e);
        } catch (TransformException e) {
            throw new RuntimeException(e);
        }
    }
    // 几何图形坐标转换
    private LiteShape2 transformGeometry(
            Geometry geom, AffineTransform transform, Rectangle paintArea) {
        try {

            IdentityHashMap<MathTransform, Decimator> decimators = new IdentityHashMap<>();
            Decimator d1 = decimators.get(transform);

            MathTransform atTransform = null;
            atTransform = ProjectiveTransform.create(transform);

            if (d1 == null)
                if (atTransform != null && !atTransform.isIdentity())
                    d1 = new Decimator(atTransform.inverse(), paintArea, 0.8);
                else d1 = new Decimator(null, paintArea, 0.8);

            geom = d1.decimateTransformGeneralize(geom, null);
            geom.geometryChanged();

            Decimator d = new Decimator(-1, -1);

            geom = d.decimateTransformGeneralize(geom, atTransform);
            geom.geometryChanged();

            return new LiteShape2(geom, null, null, false, false);

        } catch (NoninvertibleTransformException e) {
            throw new RuntimeException(e);
        } catch (FactoryException e) {
            throw new RuntimeException(e);
        } catch (TransformException e) {
            throw new RuntimeException(e);
        }
    }

    // 默认多边形样式
    private void renderDefaultPolygon(
            Graphics2D g, SVRenderLayer render_layer, LiteShape2 shape, StringMap stringMap) {

        if (shape.getGeometry() instanceof MultiPolygon
                && shape.getGeometry().getNumGeometries() > 1) {
            MultiPolygon mp = (MultiPolygon) shape.getGeometry();
            for (int i = 0; i < mp.getNumGeometries(); i++) {
                Polygon simplified = (Polygon) mp.getGeometryN(i);
                try {

                    DoubleVector doubleVector = new DoubleVector();
                    DoubleDoubleVector v_doubleVectorInter = new DoubleDoubleVector();
                    Coordinate[] exteriorcoords = simplified.getExteriorRing().getCoordinates();
                    for (Coordinate coord : exteriorcoords) {
                        doubleVector.add(coord.x);
                        doubleVector.add(coord.y);
                    }
                    // 内环坐标（孔洞）
                    for (int j = 0; j < simplified.getNumInteriorRing(); j++) {
                        Coordinate[] interiorcoords =
                                simplified.getInteriorRingN(j).getCoordinates();
                        DoubleVector doubleVectorInter = new DoubleVector();
                        for (Coordinate coord : interiorcoords) {
                            doubleVectorInter.add(coord.x);
                            doubleVectorInter.add(coord.y);
                        }
                        v_doubleVectorInter.add(doubleVectorInter);
                    }
                    render_layer.renderPolygon(doubleVector, v_doubleVectorInter, stringMap);
                } catch (Exception e) {
                    // should not really happen, but anyways
                    throw new RuntimeException(
                            "Unexpected error occurred while rendering a multipolygon", e);
                }
            }
        } else {

            Polygon simplified = (Polygon) shape.getGeometry();
            // 外环坐标
            DoubleVector doubleVector = new DoubleVector();
            DoubleDoubleVector v_doubleVectorInter = new DoubleDoubleVector();

            Coordinate[] exteriorcoords = simplified.getExteriorRing().getCoordinates();
            doubleVector.reserve(exteriorcoords.length * 2);
            for (Coordinate coord : exteriorcoords) {
                doubleVector.add(coord.x);
                doubleVector.add(coord.y);
            }
            // 内环坐标（孔洞）
            v_doubleVectorInter.reserve(simplified.getNumInteriorRing());
            for (int j = 0; j < simplified.getNumInteriorRing(); j++) {
                Coordinate[] interiorcoords = simplified.getInteriorRingN(j).getCoordinates();
                DoubleVector doubleVectorInter = new DoubleVector();
                doubleVectorInter.reserve(interiorcoords.length * 2);
                for (Coordinate coord : interiorcoords) {
                    doubleVectorInter.add(coord.x);
                    doubleVectorInter.add(coord.y);
                }
                v_doubleVectorInter.add(doubleVectorInter);
            }
            render_layer.renderPolygon(doubleVector, v_doubleVectorInter, stringMap);
        }
    }

    private void renderDefaultLine(
            Graphics2D g, SVRenderLayer layer, LiteShape2 shape, StringMap stringMap) {

        if (shape.getGeometry() instanceof MultiLineString
                && shape.getGeometry().getNumGeometries() > 1) {
            MultiLineString ml = (MultiLineString) shape.getGeometry();
            for (int i = 0; i < ml.getNumGeometries(); i++) {
                LineString l = (LineString) ml.getGeometryN(i);
                try {

                    DoubleVector doubleVector = new DoubleVector();
                    doubleVector.reserve(l.getNumPoints() * 2);
                    Coordinate[] coords = l.getCoordinates();
                    for (Coordinate coord : coords) {
                        doubleVector.add(coord.x);
                        doubleVector.add(coord.y);
                    }
                    layer.renderLine(doubleVector, stringMap);
                    // g.fill(new LiteShape2(p, null, null, false, false));
                } catch (Exception e) {
                    // should not really happen, but anyways
                    throw new RuntimeException(
                            "Unexpected error occurred while rendering a multipolygon", e);
                }
            }
        } else {
            DoubleVector doubleVector = new DoubleVector();

            LineString simplified = (LineString) shape.getGeometry();
            doubleVector.reserve(simplified.getNumPoints() * 2);

            Coordinate[] coords = simplified.getCoordinates();
            for (Coordinate coord : coords) {
                doubleVector.add(coord.x);
                doubleVector.add(coord.y);
            }
            boolean is_ok = layer.renderLine(doubleVector, stringMap);
            if (!is_ok) {
                System.out.println("Error rendering a line");
            }
        }
    }

    private void renderDefaultPoint(
            Graphics2D g, SVRenderLayer layer, LiteShape2 shape, StringMap stringMap) {

        Rectangle2D bounds = shape.getBounds2D();

        Coordinate coordinate = shape.getGeometry().getCoordinate();

        boolean is_ok = layer.renderPoint(coordinate.x, coordinate.y, stringMap);
        if (!is_ok) {
            System.out.println("Error rendering a point");
        }
    }

    public RenderedImage getImage() {

        int w = wmsMapContent.getMapWidth();
        int h = wmsMapContent.getMapHeight();
        BufferedImage result = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = result.createGraphics();
        g2d.setComposite(AlphaComposite.Clear);
        g2d.fillRect(0, 0, w, h);
        g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
        g2d.setComposite(AlphaComposite.SrcOver);

        IntVector pixels = list_render.mergeImage();

        //  if (h < 800) {
        //     h = 800;
        // }
        BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);

        for (int y = 0; y < h; y++) {
            for (int x = 0; x < w; x++) {
                int index = y * w + x;
                int argb = pixels.get(index);
                image.setRGB(x, y, argb);
            }
        }

        g2d.drawImage(image, 0, 0, null);

        g2d.dispose();
        // list_render.renderEndLayer("");

        return result;
    }

    public void setWorkspace(String workspace, String styleName) {
        if (this.workspace.equals(workspace)) {
            return;
        }

        this.workspace = workspace;
    }
    // 顺序保障的线程工厂
    private static class OrderedThreadFactory implements ThreadFactory {
        private final AtomicInteger counter = new AtomicInteger();

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r);
            t.setPriority(Thread.NORM_PRIORITY);
            t.setName("RenderThread-" + counter.getAndIncrement());
            return t;
        }
    }
}
